package com.jy.rhin.support.interceptor.convertor;

import com.jy.rhin.application.enums.*;
import com.jy.rhin.sdk.dto.audit.*;
import lombok.Data;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * @Description: 公共转换器
 * @Author: lww
 * @Date: 2022/6/23
 */
public abstract class BaseAuditInfoConverter<Req, Resp> implements AuditInfoConvert<Req, Resp> {

    private static final String XML_STR = "xmlStr";

    private EventActionCodeEnum actionCode;
    private ServiceCodeEnum serviceCode;
    private EventTypeCodeEnum eventTypeCode;
    private boolean minGanXing;
    private ParticipantObjectIDTypeCodeEnum objectIdType;
    private ParticipantObjectTypeCodeRoleEnum objectRole;
    private ParticipantObjectTypeCodeEnum objectTypeCode;
    private ParticipantObjectDataLifeCycleEnum objectLifeCycle;

    BaseAuditInfoConverter(EventActionCodeEnum actionCode,
                           ServiceCodeEnum serviceCode,
                           EventTypeCodeEnum eventTypeCode,
                           boolean minGanXing,
                           ParticipantObjectIDTypeCodeEnum objectIdType,
                           ParticipantObjectTypeCodeRoleEnum objectRole,
                           ParticipantObjectTypeCodeEnum objectTypeCode,
                           ParticipantObjectDataLifeCycleEnum objectLifeCycle) {
        this.actionCode = actionCode;
        this.serviceCode = serviceCode;
        this.eventTypeCode = eventTypeCode;
        this.minGanXing = minGanXing;
        this.objectIdType = objectIdType;
        this.objectRole = objectRole;
        this.objectTypeCode = objectTypeCode;
        this.objectLifeCycle = objectLifeCycle;
    }

    @Override
    public AuditWrapperDto convertReq(Req req) {
        AuditWrapperDto dto = new AuditWrapperDto();
        dto.setEventInfo(buildEventInfo());
        AuditParticipantUserDesc sourceUserReq = buildSourceUser(req, null);
        AuditParticipantUserDesc originatorUserReq = buildOriginatorUser(req, null);
        AuditParticipantUserDesc targetUserReq = buildTargetUser(req, null);
        dto.setParticipantList(buildParticipant(sourceUserReq, originatorUserReq, targetUserReq));
        dto.setSourceList(buildAuditSource());
        dto.setObjectDtoList(buildAuditObject(req, null));
        return dto;
    }

    @Override
    public AuditWrapperDto convertResp(Resp resp) {
        AuditWrapperDto dto = new AuditWrapperDto();
        dto.setEventInfo(buildEventInfo());
        AuditParticipantUserDesc sourceUserReq = buildSourceUser(null, resp);
        AuditParticipantUserDesc originatorUserReq = buildOriginatorUser(null, resp);
        AuditParticipantUserDesc targetUserReq = buildTargetUser(null, resp);
        dto.setParticipantList(buildParticipant(sourceUserReq, originatorUserReq, targetUserReq));
        dto.setSourceList(buildAuditSource());
        dto.setObjectDtoList(buildAuditObject(null, resp));
        return dto;
    }

    /**
     * 事件信息
     */
    private AuditInfoEventDto buildEventInfo() {
        AuditInfoEventDto eventInfo = new AuditInfoEventDto();
        eventInfo.setCaoZuoLx(actionCode.getCode());
        eventInfo.setFaQiSj(new Date());
        eventInfo.setShiJianBs(buildEventId());
        eventInfo.setShiJianLx(buildEventTypeCode());
        eventInfo.setShiJianJg(EventOutcomeIndicatorEnum.SUCCESS.getCode());
        return eventInfo;
    }

    /**
     * 事件类型
     *
     * @return
     */
    private CodedValueTypeDto buildEventTypeCode() {
        CodedValueTypeDto code = new CodedValueTypeDto();
        code.setCodeSystem(CodeSystemEnum.CV_EVENT_TYPE_CODE.getCode());
        code.setCodeSystemName(CodeSystemEnum.CV_EVENT_TYPE_CODE.getName());
        code.setCode(this.eventTypeCode.getCode());
        code.setDisplayName(this.eventTypeCode.getName());
        return code;
    }

    /**
     * 事件标识
     *
     * @return
     */
    private CodedValueTypeDto buildEventId() {
        CodedValueTypeDto eventId = new CodedValueTypeDto();
        eventId.setCodeSystem(CodeSystemEnum.CV_EVENT_ID_CODE.getCode());
        eventId.setCodeSystemName(CodeSystemEnum.CV_EVENT_ID_CODE.getName());
        eventId.setCode(serviceCode.getCode());
        eventId.setDisplayName(serviceCode.getName());
        return eventId;
    }

    /**
     * 参与者角色
     *
     * @param codeEnum
     * @return
     */
    private CodedValueTypeDto buildParticipantRole(RoleIDCodeEnum codeEnum) {
        CodedValueTypeDto sourceRoleCode = new CodedValueTypeDto();
        sourceRoleCode.setCodeSystem(CodeSystemEnum.CV_ROLE_ID_CODE.getCode());
        sourceRoleCode.setCodeSystemName(CodeSystemEnum.CV_ROLE_ID_CODE.getName());
        sourceRoleCode.setCode(codeEnum.getCode());
        sourceRoleCode.setDisplayName(codeEnum.getName());
        return sourceRoleCode;
    }

    /**
     * 具体参与者对象
     */
    private List<AuditObjectDto> buildAuditObject(Req req, Resp resp) {
        String objectId = getObjectId(req, resp);

        AuditObjectDto auditObject = new AuditObjectDto();

        auditObject.setObjectId(objectId);
        auditObject.setObjectLx(objectTypeCode.getCode());
        auditObject.setObjectJueSe(objectRole.getCode());
        auditObject.setObjectZhouQi(objectLifeCycle.getCode());
        auditObject.setMinGanXing(minGanXing ? "Y" : "N");
        auditObject.setBiaoShiLx(buildParticipantType());
        auditObject.setObjectName(eventTypeCode.getName());

        AuditObjectDto.AuditObjectDesc auditObjectDesc = new AuditObjectDto.AuditObjectDesc();
        auditObjectDesc.setType(XML_STR);
        auditObject.setMiaoShu(auditObjectDesc);

        return Collections.singletonList(auditObject);
    }

    /**
     * 参与者类型
     *
     * @return
     */
    private CodedValueTypeDto buildParticipantType() {
        CodedValueTypeDto objectIdTypeCode = new CodedValueTypeDto();
        objectIdTypeCode.setCodeSystem(CodeSystemEnum.CV_PARTICIPANT_OBJECT_TYPE_CODE.getCode());
        objectIdTypeCode.setCodeSystemName(CodeSystemEnum.CV_PARTICIPANT_OBJECT_TYPE_CODE.getName());
        objectIdTypeCode.setCode(objectIdType.getCode());
        objectIdTypeCode.setDisplayName(objectIdType.getName());
        return objectIdTypeCode;
    }


    /**
     * 构建审计事件参与者
     *
     * @param sourceUser     事件源用户
     * @param originatorUser 事件发起人
     * @param targetUser     事件目标用户
     * @return
     */
    private List<AuditParticipantDto> buildParticipant(AuditParticipantUserDesc sourceUser, AuditParticipantUserDesc originatorUser, AuditParticipantUserDesc targetUser) {
        List<AuditParticipantDto> participantList = new ArrayList<>();
        AuditParticipantDto source = new AuditParticipantDto();
        source.setUserId(sourceUser.userId);
        source.setUserName(sourceUser.username);
        source.setUserIsRequester(true);
        source.setJueSe(buildParticipantRole(RoleIDCodeEnum.SOURCE));
        participantList.add(source);

        AuditParticipantDto originator = new AuditParticipantDto();
        originator.setUserId(originatorUser.userId);
        originator.setUserName(originatorUser.username);
        originator.setUserIsRequester(true);
        originator.setJueSe(buildParticipantRole(RoleIDCodeEnum.APPLICATION));
        participantList.add(originator);

        AuditParticipantDto target = new AuditParticipantDto();
        target.setUserId(targetUser.userId);
        target.setUserName(targetUser.username);
        target.setUserIsRequester(false);
        target.setJueSe(buildParticipantRole(RoleIDCodeEnum.DESTINATION));
        target.setWangLuoFwd(getHostName());
        target.setWangLuoFwdCode(NetworkAccessPointTypeCodeEnum.IP_ADDRESS.getCode());
        participantList.add(target);

        return participantList;
    }

    /**
     * 获取host地址
     * @return
     */
    private String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            return "";
        }
    }

    /**
     * 初始化
     *
     * @return
     */
    abstract List<AuditSourceDto> buildAuditSource();


    /**
     * 构建参与者的用户信息
     *
     * @param req
     * @param resp
     * @return
     */
    abstract AuditParticipantUserDesc buildSourceUser(Req req, Resp resp);

    /**
     * 构建参与者的用户信息
     *
     * @param req
     * @param resp
     * @return
     */
    abstract AuditParticipantUserDesc buildOriginatorUser(Req req, Resp resp);

    /**
     * 构建参与者的用户信息
     *
     * @param req
     * @param resp
     * @return
     */
    abstract AuditParticipantUserDesc buildTargetUser(Req req, Resp resp);

    /**
     * 根据id获取
     *
     * @param req
     * @param resp
     * @return
     */
    abstract String getObjectId(Req req, Resp resp);

    @Data
    static class AuditParticipantUserDesc {

        public AuditParticipantUserDesc(String userId) {
            this.userId = userId;
        }

        public AuditParticipantUserDesc(String userId, String username) {
            this.userId = userId;
            this.username = username;
        }

        String userId;
        String username;
    }

}
